/*!
 * HTML5 export buttons for Buttons and DataTables.
 * 2016 SpryMedia Ltd - datatables.net/license
 *
 * FileSaver.js (1.3.3) - MIT license
 * Copyright © 2016 Eli Grey - http://eligrey.com
 */

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD
        define(['jquery', 'datatables.net', 'datatables.net-buttons'], function ($) {
            return factory($, window, document);
        });
    } else if (typeof exports === 'object') {
        // CommonJS
        module.exports = function (root, $, jszip, pdfmake) {
            if (!root) {
                root = window;
            }

            if (!$ || !$.fn.dataTable) {
                $ = require('datatables.net')(root, $).$;
            }

            if (!$.fn.dataTable.Buttons) {
                require('datatables.net-buttons')(root, $);
            }

            return factory($, root, root.document, jszip, pdfmake);
        };
    } else {
        // Browser
        factory(jQuery, window, document);
    }
}(function ($, window, document, jszip, pdfmake, undefined) {
    'use strict';
    var DataTable = $.fn.dataTable;

// Allow the constructor to pass in JSZip and PDFMake from external requires.
// Otherwise, use globally defined variables, if they are available.
    function _jsZip() {
        return jszip || window.JSZip;
    }

    function _pdfMake() {
        return pdfmake || window.pdfMake;
    }

    DataTable.Buttons.pdfMake = function (_) {
        if (!_) {
            return _pdfMake();
        }
        pdfmake = _;
    }

    DataTable.Buttons.jszip = function (_) {
        if (!_) {
            return _jsZip();
        }
        jszip = _;
    }


    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
     * FileSaver.js dependency
     */

    /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */

    var _saveAs = (function (view) {
        "use strict";
        // IE <10 is explicitly unsupported
        if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
            return;
        }
        var
            doc = view.document
            // only get URL when necessary in case Blob.js hasn't overridden it yet
            , get_URL = function () {
                return view.URL || view.webkitURL || view;
            }
            , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
            , can_use_save_link = "download" in save_link
            , click = function (node) {
                var event = new MouseEvent("click");
                node.dispatchEvent(event);
            }
            , is_safari = /constructor/i.test(view.HTMLElement) || view.safari
            , is_chrome_ios = /CriOS\/[\d]+/.test(navigator.userAgent)
            , throw_outside = function (ex) {
                (view.setImmediate || view.setTimeout)(function () {
                    throw ex;
                }, 0);
            }
            , force_saveable_type = "application/octet-stream"
            // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
            , arbitrary_revoke_timeout = 1000 * 40 // in ms
            , revoke = function (file) {
                var revoker = function () {
                    if (typeof file === "string") { // file is an object URL
                        get_URL().revokeObjectURL(file);
                    } else { // file is a File
                        file.remove();
                    }
                };
                setTimeout(revoker, arbitrary_revoke_timeout);
            }
            , dispatch = function (filesaver, event_types, event) {
                event_types = [].concat(event_types);
                var i = event_types.length;
                while (i--) {
                    var listener = filesaver["on" + event_types[i]];
                    if (typeof listener === "function") {
                        try {
                            listener.call(filesaver, event || filesaver);
                        } catch (ex) {
                            throw_outside(ex);
                        }
                    }
                }
            }
            , auto_bom = function (blob) {
                // prepend BOM for UTF-8 XML and text/* types (including HTML)
                // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
                if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
                    return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type});
                }
                return blob;
            }
            , FileSaver = function (blob, name, no_auto_bom) {
                if (!no_auto_bom) {
                    blob = auto_bom(blob);
                }
                // First try a.download, then web filesystem, then object URLs
                var
                    filesaver = this
                    , type = blob.type
                    , force = type === force_saveable_type
                    , object_url
                    , dispatch_all = function () {
                        dispatch(filesaver, "writestart progress write writeend".split(" "));
                    }
                    // on any filesys errors revert to saving with object URLs
                    , fs_error = function () {
                        if ((is_chrome_ios || (force && is_safari)) && view.FileReader) {
                            // Safari doesn't allow downloading of blob urls
                            var reader = new FileReader();
                            reader.onloadend = function () {
                                var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;');
                                var popup = view.open(url, '_blank');
                                if (!popup) view.location.href = url;
                                url = undefined; // release reference before dispatching
                                filesaver.readyState = filesaver.DONE;
                                dispatch_all();
                            };
                            reader.readAsDataURL(blob);
                            filesaver.readyState = filesaver.INIT;
                            return;
                        }
                        // don't create more object URLs than needed
                        if (!object_url) {
                            object_url = get_URL().createObjectURL(blob);
                        }
                        if (force) {
                            view.location.href = object_url;
                        } else {
                            var opened = view.open(object_url, "_blank");
                            if (!opened) {
                                // Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
                                view.location.href = object_url;
                            }
                        }
                        filesaver.readyState = filesaver.DONE;
                        dispatch_all();
                        revoke(object_url);
                    }
                ;
                filesaver.readyState = filesaver.INIT;

                if (can_use_save_link) {
                    object_url = get_URL().createObjectURL(blob);
                    setTimeout(function () {
                        save_link.href = object_url;
                        save_link.download = name;
                        click(save_link);
                        dispatch_all();
                        revoke(object_url);
                        filesaver.readyState = filesaver.DONE;
                    });
                    return;
                }

                fs_error();
            }
            , FS_proto = FileSaver.prototype
            , saveAs = function (blob, name, no_auto_bom) {
                return new FileSaver(blob, name || blob.name || "download", no_auto_bom);
            }
        ;
        // IE 10+ (native saveAs)
        if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
            return function (blob, name, no_auto_bom) {
                name = name || blob.name || "download";

                if (!no_auto_bom) {
                    blob = auto_bom(blob);
                }
                return navigator.msSaveOrOpenBlob(blob, name);
            };
        }

        FS_proto.abort = function () {
        };
        FS_proto.readyState = FS_proto.INIT = 0;
        FS_proto.WRITING = 1;
        FS_proto.DONE = 2;

        FS_proto.error =
            FS_proto.onwritestart =
                FS_proto.onprogress =
                    FS_proto.onwrite =
                        FS_proto.onabort =
                            FS_proto.onerror =
                                FS_proto.onwriteend =
                                    null;

        return saveAs;
    }(
        typeof self !== "undefined" && self
        || typeof window !== "undefined" && window
        || this.content
    ));


// Expose file saver on the DataTables API. Can't attach to `DataTables.Buttons`
// since this file can be loaded before Button's core!
    DataTable.fileSave = _saveAs;


    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
     * Local (private) functions
     */

    /**
     * Get the sheet name for Excel exports.
     *
     * @param {object}    config Button configuration
     */
    var _sheetname = function (config) {
        var sheetName = 'Sheet1';

        if (config.sheetName) {
            sheetName = config.sheetName.replace(/[\[\]\*\/\\\?\:]/g, '');
        }

        return sheetName;
    };

    /**
     * Get the newline character(s)
     *
     * @param {object}    config Button configuration
     * @return {string}                Newline character
     */
    var _newLine = function (config) {
        return config.newline ?
            config.newline :
            navigator.userAgent.match(/Windows/) ?
                '\r\n' :
                '\n';
    };

    /**
     * Combine the data from the `buttons.exportData` method into a string that
     * will be used in the export file.
     *
     * @param    {DataTable.Api} dt         DataTables API instance
     * @param    {object}                config Button configuration
     * @return {object}                             The data to export
     */
    var _exportData = function (dt, config) {
        var newLine = _newLine(config);
        var data = dt.buttons.exportData(config.exportOptions);
        var boundary = config.fieldBoundary;
        var separator = config.fieldSeparator;
        var reBoundary = new RegExp(boundary, 'g');
        var escapeChar = config.escapeChar !== undefined ?
            config.escapeChar :
            '\\';
        var join = function (a) {
            var s = '';

            // If there is a field boundary, then we might need to escape it in
            // the source data
            for (var i = 0, ien = a.length; i < ien; i++) {
                if (i > 0) {
                    s += separator;
                }

                s += boundary ?
                    boundary + ('' + a[i]).replace(reBoundary, escapeChar + boundary) + boundary :
                    a[i];
            }

            return s;
        };

        var header = config.header ? join(data.header) + newLine : '';
        var footer = config.footer && data.footer ? newLine + join(data.footer) : '';
        var body = [];

        for (var i = 0, ien = data.body.length; i < ien; i++) {
            body.push(join(data.body[i]));
        }

        return {
            str: header + body.join(newLine) + footer,
            rows: body.length
        };
    };

    /**
     * Older versions of Safari (prior to tech preview 18) don't support the
     * download option required.
     *
     * @return {Boolean} `true` if old Safari
     */
    var _isDuffSafari = function () {
        var safari = navigator.userAgent.indexOf('Safari') !== -1 &&
            navigator.userAgent.indexOf('Chrome') === -1 &&
            navigator.userAgent.indexOf('Opera') === -1;

        if (!safari) {
            return false;
        }

        var version = navigator.userAgent.match(/AppleWebKit\/(\d+\.\d+)/);
        if (version && version.length > 1 && version[1] * 1 < 603.1) {
            return true;
        }

        return false;
    };

    /**
     * Convert from numeric position to letter for column names in Excel
     * @param  {int} n Column number
     * @return {string} Column letter(s) name
     */
    function createCellPos(n) {
        var ordA = 'A'.charCodeAt(0);
        var ordZ = 'Z'.charCodeAt(0);
        var len = ordZ - ordA + 1;
        var s = "";

        while (n >= 0) {
            s = String.fromCharCode(n % len + ordA) + s;
            n = Math.floor(n / len) - 1;
        }

        return s;
    }

    try {
        var _serialiser = new XMLSerializer();
        var _ieExcel;
    } catch (t) {
    }

    /**
     * Recursively add XML files from an object's structure to a ZIP file. This
     * allows the XSLX file to be easily defined with an object's structure matching
     * the files structure.
     *
     * @param {JSZip} zip ZIP package
     * @param {object} obj Object to add (recursive)
     */
    function _addToZip(zip, obj) {
        if (_ieExcel === undefined) {
            // Detect if we are dealing with IE's _awful_ serialiser by seeing if it
            // drop attributes
            _ieExcel = _serialiser
                .serializeToString(
                    (new window.DOMParser()).parseFromString(excelStrings['xl/worksheets/sheet1.xml'], 'text/xml')
                )
                .indexOf('xmlns:r') === -1;
        }

        $.each(obj, function (name, val) {
            if ($.isPlainObject(val)) {
                var newDir = zip.folder(name);
                _addToZip(newDir, val);
            } else {
                if (_ieExcel) {
                    // IE's XML serialiser will drop some name space attributes from
                    // from the root node, so we need to save them. Do this by
                    // replacing the namespace nodes with a regular attribute that
                    // we convert back when serialised. Edge does not have this
                    // issue
                    var worksheet = val.childNodes[0];
                    var i, ien;
                    var attrs = [];

                    for (i = worksheet.attributes.length - 1; i >= 0; i--) {
                        var attrName = worksheet.attributes[i].nodeName;
                        var attrValue = worksheet.attributes[i].nodeValue;

                        if (attrName.indexOf(':') !== -1) {
                            attrs.push({name: attrName, value: attrValue});

                            worksheet.removeAttribute(attrName);
                        }
                    }

                    for (i = 0, ien = attrs.length; i < ien; i++) {
                        var attr = val.createAttribute(attrs[i].name.replace(':', '_dt_b_namespace_token_'));
                        attr.value = attrs[i].value;
                        worksheet.setAttributeNode(attr);
                    }
                }

                var str = _serialiser.serializeToString(val);

                // Fix IE's XML
                if (_ieExcel) {
                    // IE doesn't include the XML declaration
                    if (str.indexOf('<?xml') === -1) {
                        str = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + str;
                    }

                    // Return namespace attributes to being as such
                    str = str.replace(/_dt_b_namespace_token_/g, ':');

                    // Remove testing name space that IE puts into the space preserve attr
                    str = str.replace(/xmlns:NS[\d]+="" NS[\d]+:/g, '');
                }

                // Safari, IE and Edge will put empty name space attributes onto
                // various elements making them useless. This strips them out
                str = str.replace(/<([^<>]*?) xmlns=""([^<>]*?)>/g, '<$1 $2>');

                zip.file(name, str);
            }
        });
    }

    /**
     * Create an XML node and add any children, attributes, etc without needing to
     * be verbose in the DOM.
     *
     * @param  {object} doc      XML document
     * @param  {string} nodeName Node name
     * @param  {object} opts     Options - can be `attr` (attributes), `children`
     *   (child nodes) and `text` (text content)
     * @return {node}            Created node
     */
    function _createNode(doc, nodeName, opts) {
        var tempNode = doc.createElement(nodeName);

        if (opts) {
            if (opts.attr) {
                $(tempNode).attr(opts.attr);
            }

            if (opts.children) {
                $.each(opts.children, function (key, value) {
                    tempNode.appendChild(value);
                });
            }

            if (opts.text !== null && opts.text !== undefined) {
                tempNode.appendChild(doc.createTextNode(opts.text));
            }
        }

        return tempNode;
    }

    /**
     * Get the width for an Excel column based on the contents of that column
     * @param  {object} data Data for export
     * @param  {int}    col  Column index
     * @return {int}         Column width
     */
    function _excelColWidth(data, col) {
        var max = data.header[col].length;
        var len, lineSplit, str;

        if (data.footer && data.footer[col].length > max) {
            max = data.footer[col].length;
        }

        for (var i = 0, ien = data.body.length; i < ien; i++) {
            var point = data.body[i][col];
            str = point !== null && point !== undefined ?
                point.toString() :
                '';

            // If there is a newline character, workout the width of the column
            // based on the longest line in the string
            if (str.indexOf('\n') !== -1) {
                lineSplit = str.split('\n');
                lineSplit.sort(function (a, b) {
                    return b.length - a.length;
                });

                len = lineSplit[0].length;
            } else {
                len = str.length;
            }

            if (len > max) {
                max = len;
            }

            // Max width rather than having potentially massive column widths
            if (max > 40) {
                return 54; // 40 * 1.35
            }
        }

        max *= 1.35;

        // And a min width
        return max > 6 ? max : 6;
    }

// Excel - Pre-defined strings to build a basic XLSX file
    var excelStrings = {
        "_rels/.rels":
            '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
            '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' +
            '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>' +
            '</Relationships>',

        "xl/_rels/workbook.xml.rels":
            '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
            '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' +
            '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>' +
            '<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>' +
            '</Relationships>',

        "[Content_Types].xml":
            '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
            '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">' +
            '<Default Extension="xml" ContentType="application/xml" />' +
            '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml" />' +
            '<Default Extension="jpeg" ContentType="image/jpeg" />' +
            '<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" />' +
            '<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml" />' +
            '<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml" />' +
            '</Types>',

        "xl/workbook.xml":
            '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
            '<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">' +
            '<fileVersion appName="xl" lastEdited="5" lowestEdited="5" rupBuild="24816"/>' +
            '<workbookPr showInkAnnotation="0" autoCompressPictures="0"/>' +
            '<bookViews>' +
            '<workbookView xWindow="0" yWindow="0" windowWidth="25600" windowHeight="19020" tabRatio="500"/>' +
            '</bookViews>' +
            '<sheets>' +
            '<sheet name="Sheet1" sheetId="1" r:id="rId1"/>' +
            '</sheets>' +
            '<definedNames/>' +
            '</workbook>',

        "xl/worksheets/sheet1.xml":
            '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
            '<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">' +
            '<sheetData/>' +
            '<mergeCells count="0"/>' +
            '</worksheet>',

        "xl/styles.xml":
            '<?xml version="1.0" encoding="UTF-8"?>' +
            '<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">' +
            '<numFmts count="6">' +
            '<numFmt numFmtId="164" formatCode="#,##0.00_-\ [$$-45C]"/>' +
            '<numFmt numFmtId="165" formatCode="&quot;£&quot;#,##0.00"/>' +
            '<numFmt numFmtId="166" formatCode="[$€-2]\ #,##0.00"/>' +
            '<numFmt numFmtId="167" formatCode="0.0%"/>' +
            '<numFmt numFmtId="168" formatCode="#,##0;(#,##0)"/>' +
            '<numFmt numFmtId="169" formatCode="#,##0.00;(#,##0.00)"/>' +
            '</numFmts>' +
            '<fonts count="5" x14ac:knownFonts="1">' +
            '<font>' +
            '<sz val="11" />' +
            '<name val="Calibri" />' +
            '</font>' +
            '<font>' +
            '<sz val="11" />' +
            '<name val="Calibri" />' +
            '<color rgb="FFFFFFFF" />' +
            '</font>' +
            '<font>' +
            '<sz val="11" />' +
            '<name val="Calibri" />' +
            '<b />' +
            '</font>' +
            '<font>' +
            '<sz val="11" />' +
            '<name val="Calibri" />' +
            '<i />' +
            '</font>' +
            '<font>' +
            '<sz val="11" />' +
            '<name val="Calibri" />' +
            '<u />' +
            '</font>' +
            '</fonts>' +
            '<fills count="6">' +
            '<fill>' +
            '<patternFill patternType="none" />' +
            '</fill>' +
            '<fill>' + // Excel appears to use this as a dotted background regardless of values but
            '<patternFill patternType="none" />' + // to be valid to the schema, use a patternFill
            '</fill>' +
            '<fill>' +
            '<patternFill patternType="solid">' +
            '<fgColor rgb="FFD9D9D9" />' +
            '<bgColor indexed="64" />' +
            '</patternFill>' +
            '</fill>' +
            '<fill>' +
            '<patternFill patternType="solid">' +
            '<fgColor rgb="FFD99795" />' +
            '<bgColor indexed="64" />' +
            '</patternFill>' +
            '</fill>' +
            '<fill>' +
            '<patternFill patternType="solid">' +
            '<fgColor rgb="ffc6efce" />' +
            '<bgColor indexed="64" />' +
            '</patternFill>' +
            '</fill>' +
            '<fill>' +
            '<patternFill patternType="solid">' +
            '<fgColor rgb="ffc6cfef" />' +
            '<bgColor indexed="64" />' +
            '</patternFill>' +
            '</fill>' +
            '</fills>' +
            '<borders count="2">' +
            '<border>' +
            '<left />' +
            '<right />' +
            '<top />' +
            '<bottom />' +
            '<diagonal />' +
            '</border>' +
            '<border diagonalUp="false" diagonalDown="false">' +
            '<left style="thin">' +
            '<color auto="1" />' +
            '</left>' +
            '<right style="thin">' +
            '<color auto="1" />' +
            '</right>' +
            '<top style="thin">' +
            '<color auto="1" />' +
            '</top>' +
            '<bottom style="thin">' +
            '<color auto="1" />' +
            '</bottom>' +
            '<diagonal />' +
            '</border>' +
            '</borders>' +
            '<cellStyleXfs count="1">' +
            '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" />' +
            '</cellStyleXfs>' +
            '<cellXfs count="68">' +
            '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="1" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="2" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="3" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="4" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="0" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="1" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="2" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="3" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="4" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="0" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="1" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="2" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="3" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="4" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="0" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="1" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="2" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="3" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="4" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="0" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="1" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="2" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="3" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="4" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="0" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="1" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="2" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="3" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="4" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="0" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="1" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="2" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="3" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="4" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="0" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="1" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="2" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="3" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="4" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="0" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="1" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="2" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="3" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="4" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="0" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="1" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="2" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="3" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="4" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
            '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
            '<alignment horizontal="left"/>' +
            '</xf>' +
            '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
            '<alignment horizontal="center"/>' +
            '</xf>' +
            '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
            '<alignment horizontal="right"/>' +
            '</xf>' +
            '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
            '<alignment horizontal="fill"/>' +
            '</xf>' +
            '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
            '<alignment textRotation="90"/>' +
            '</xf>' +
            '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
            '<alignment wrapText="1"/>' +
            '</xf>' +
            '<xf numFmtId="9"   fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
            '<xf numFmtId="164" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
            '<xf numFmtId="165" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
            '<xf numFmtId="166" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
            '<xf numFmtId="167" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
            '<xf numFmtId="168" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
            '<xf numFmtId="169" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
            '<xf numFmtId="3" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
            '<xf numFmtId="4" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
            '<xf numFmtId="1" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
            '<xf numFmtId="2" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
            '<xf numFmtId="14" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
            '</cellXfs>' +
            '<cellStyles count="1">' +
            '<cellStyle name="Normal" xfId="0" builtinId="0" />' +
            '</cellStyles>' +
            '<dxfs count="0" />' +
            '<tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleMedium4" />' +
            '</styleSheet>'
    };
// Note we could use 3 `for` loops for the styles, but when gzipped there is
// virtually no difference in size, since the above can be easily compressed

// Pattern matching for special number formats. Perhaps this should be exposed
// via an API in future?
// Ref: section 3.8.30 - built in formatters in open spreadsheet
//   https://www.ecma-international.org/news/TC45_current_work/Office%20Open%20XML%20Part%204%20-%20Markup%20Language%20Reference.pdf
    var _excelSpecials = [
        {
            match: /^\-?\d+\.\d%$/, style: 60, fmt: function (d) {
                return d / 100;
            }
        }, // Precent with d.p.
        {
            match: /^\-?\d+\.?\d*%$/, style: 56, fmt: function (d) {
                return d / 100;
            }
        }, // Percent
        {match: /^\-?\$[\d,]+.?\d*$/, style: 57}, // Dollars
        {match: /^\-?£[\d,]+.?\d*$/, style: 58}, // Pounds
        {match: /^\-?€[\d,]+.?\d*$/, style: 59}, // Euros
        {match: /^\-?\d+$/, style: 65}, // Numbers without thousand separators
        {match: /^\-?\d+\.\d{2}$/, style: 66}, // Numbers 2 d.p. without thousands separators
        {
            match: /^\([\d,]+\)$/, style: 61, fmt: function (d) {
                return -1 * d.replace(/[\(\)]/g, '');
            }
        },  // Negative numbers indicated by brackets
        {
            match: /^\([\d,]+\.\d{2}\)$/, style: 62, fmt: function (d) {
                return -1 * d.replace(/[\(\)]/g, '');
            }
        },  // Negative numbers indicated by brackets - 2d.p.
        {match: /^\-?[\d,]+$/, style: 63}, // Numbers with thousand separators
        {match: /^\-?[\d,]+\.\d{2}$/, style: 64},
        {
            match: /^[\d]{4}\-[\d]{2}\-[\d]{2}$/, style: 67, fmt: function (d) {
                return Math.round(25569 + (Date.parse(d) / (86400 * 1000)));
            }
        } //Date yyyy-mm-dd
    ];


    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
     * Buttons
     */

//
// Copy to clipboard
//
    DataTable.ext.buttons.copyHtml5 = {
        className: 'buttons-copy buttons-html5',

        text: function (dt) {
            return dt.i18n('buttons.copy', 'Copy');
        },

        action: function (e, dt, button, config) {
            this.processing(true);

            var that = this;
            var exportData = _exportData(dt, config);
            var info = dt.buttons.exportInfo(config);
            var newline = _newLine(config);
            var output = exportData.str;
            var hiddenDiv = $('<div/>')
                .css({
                    height: 1,
                    width: 1,
                    overflow: 'hidden',
                    position: 'fixed',
                    top: 0,
                    left: 0
                });

            if (info.title) {
                output = info.title + newline + newline + output;
            }

            if (info.messageTop) {
                output = info.messageTop + newline + newline + output;
            }

            if (info.messageBottom) {
                output = output + newline + newline + info.messageBottom;
            }

            if (config.customize) {
                output = config.customize(output, config, dt);
            }

            var textarea = $('<textarea readonly/>')
                .val(output)
                .appendTo(hiddenDiv);

            // For browsers that support the copy execCommand, try to use it
            if (document.queryCommandSupported('copy')) {
                hiddenDiv.appendTo(dt.table().container());
                textarea[0].focus();
                textarea[0].select();

                try {
                    var successful = document.execCommand('copy');
                    hiddenDiv.remove();

                    if (successful) {
                        dt.buttons.info(
                            dt.i18n('buttons.copyTitle', 'Copy to clipboard'),
                            dt.i18n('buttons.copySuccess', {
                                1: 'Copied one row to clipboard',
                                _: 'Copied %d rows to clipboard'
                            }, exportData.rows),
                            2000
                        );

                        this.processing(false);
                        return;
                    }
                } catch (t) {
                }
            }

            // Otherwise we show the text box and instruct the user to use it
            var message = $('<span>' + dt.i18n('buttons.copyKeys',
                'Press <i>ctrl</i> or <i>\u2318</i> + <i>C</i> to copy the table data<br>to your system clipboard.<br><br>' +
                'To cancel, click this message or press escape.') + '</span>'
            )
                .append(hiddenDiv);

            dt.buttons.info(dt.i18n('buttons.copyTitle', 'Copy to clipboard'), message, 0);

            // Select the text so when the user activates their system clipboard
            // it will copy that text
            textarea[0].focus();
            textarea[0].select();

            // Event to hide the message when the user is done
            var container = $(message).closest('.dt-button-info');
            var close = function () {
                container.off('click.buttons-copy');
                $(document).off('.buttons-copy');
                dt.buttons.info(false);
            };

            container.on('click.buttons-copy', close);
            $(document)
                .on('keydown.buttons-copy', function (e) {
                    if (e.keyCode === 27) { // esc
                        close();
                        that.processing(false);
                    }
                })
                .on('copy.buttons-copy cut.buttons-copy', function () {
                    close();
                    that.processing(false);
                });
        },

        exportOptions: {},

        fieldSeparator: '\t',

        fieldBoundary: '',

        header: true,

        footer: false,

        title: '*',

        messageTop: '*',

        messageBottom: '*'
    };

//
// CSV export
//
    DataTable.ext.buttons.csvHtml5 = {
        bom: false,

        className: 'buttons-csv buttons-html5',

        available: function () {
            return window.FileReader !== undefined && window.Blob;
        },

        text: function (dt) {
            return dt.i18n('buttons.csv', 'CSV');
        },

        action: function (e, dt, button, config) {
            this.processing(true);

            // Set the text
            var output = _exportData(dt, config).str;
            var info = dt.buttons.exportInfo(config);
            var charset = config.charset;

            if (config.customize) {
                output = config.customize(output, config, dt);
            }

            if (charset !== false) {
                if (!charset) {
                    charset = document.characterSet || document.charset;
                }

                if (charset) {
                    charset = ';charset=' + charset;
                }
            } else {
                charset = '';
            }

            if (config.bom) {
                output = '\ufeff' + output;
            }

            _saveAs(
                new Blob([output], {type: 'text/csv' + charset}),
                info.filename,
                true
            );

            this.processing(false);
        },

        filename: '*',

        extension: '.csv',

        exportOptions: {},

        fieldSeparator: ',',

        fieldBoundary: '"',

        escapeChar: '"',

        charset: null,

        header: true,

        footer: false
    };

//
// Excel (xlsx) export
//
    DataTable.ext.buttons.excelHtml5 = {
        className: 'buttons-excel buttons-html5',

        available: function () {
            return window.FileReader !== undefined && _jsZip() !== undefined && !_isDuffSafari() && _serialiser;
        },

        text: function (dt) {
            return dt.i18n('buttons.excel', 'Excel');
        },

        action: function (e, dt, button, config) {
            this.processing(true);

            var that = this;
            var rowPos = 0;
            var dataStartRow, dataEndRow;
            var getXml = function (type) {
                var str = excelStrings[type];

                //str = str.replace( /xmlns:/g, 'xmlns_' ).replace( /mc:/g, 'mc_' );

                return $.parseXML(str);
            };
            var rels = getXml('xl/worksheets/sheet1.xml');
            var relsGet = rels.getElementsByTagName("sheetData")[0];

            var xlsx = {
                _rels: {
                    ".rels": getXml('_rels/.rels')
                },
                xl: {
                    _rels: {
                        "workbook.xml.rels": getXml('xl/_rels/workbook.xml.rels')
                    },
                    "workbook.xml": getXml('xl/workbook.xml'),
                    "styles.xml": getXml('xl/styles.xml'),
                    "worksheets": {
                        "sheet1.xml": rels
                    }

                },
                "[Content_Types].xml": getXml('[Content_Types].xml')
            };

            var data = dt.buttons.exportData(config.exportOptions);
            var currentRow, rowNode;
            var addRow = function (row) {
                currentRow = rowPos + 1;
                rowNode = _createNode(rels, "row", {attr: {r: currentRow}});

                for (var i = 0, ien = row.length; i < ien; i++) {
                    // Concat both the Cell Columns as a letter and the Row of the cell.
                    var cellId = createCellPos(i) + '' + currentRow;
                    var cell = null;

                    // For null, undefined of blank cell, continue so it doesn't create the _createNode
                    if (row[i] === null || row[i] === undefined || row[i] === '') {
                        if (config.createEmptyCells === true) {
                            row[i] = '';
                        } else {
                            continue;
                        }
                    }

                    var originalContent = row[i];
                    row[i] = row[i].trim();

                    // Special number formatting options
                    for (var j = 0, jen = _excelSpecials.length; j < jen; j++) {
                        var special = _excelSpecials[j];

                        // TODO Need to provide the ability for the specials to say
                        // if they are returning a string, since at the moment it is
                        // assumed to be a number
                        if (row[i].match && !row[i].match(/^0\d+/) && row[i].match(special.match)) {
                            var val = row[i].replace(/[^\d\.\-]/g, '');

                            if (special.fmt) {
                                val = special.fmt(val);
                            }

                            cell = _createNode(rels, 'c', {
                                attr: {
                                    r: cellId,
                                    s: special.style
                                },
                                children: [
                                    _createNode(rels, 'v', {text: val})
                                ]
                            });

                            break;
                        }
                    }

                    if (!cell) {
                        if (typeof row[i] === 'number' || (
                            row[i].match &&
                            row[i].match(/^-?\d+(\.\d+)?$/) &&
                            !row[i].match(/^0\d+/))
                        ) {
                            // Detect numbers - don't match numbers with leading zeros
                            // or a negative anywhere but the start
                            cell = _createNode(rels, 'c', {
                                attr: {
                                    t: 'n',
                                    r: cellId
                                },
                                children: [
                                    _createNode(rels, 'v', {text: row[i]})
                                ]
                            });
                        } else {
                            // String output - replace non standard characters for text output
                            var text = !originalContent.replace ?
                                originalContent :
                                originalContent.replace(/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F-\x9F]/g, '');

                            cell = _createNode(rels, 'c', {
                                attr: {
                                    t: 'inlineStr',
                                    r: cellId
                                },
                                children: {
                                    row: _createNode(rels, 'is', {
                                        children: {
                                            row: _createNode(rels, 't', {
                                                text: text,
                                                attr: {
                                                    'xml:space': 'preserve'
                                                }
                                            })
                                        }
                                    })
                                }
                            });
                        }
                    }

                    rowNode.appendChild(cell);
                }

                relsGet.appendChild(rowNode);
                rowPos++;
            };

            if (config.customizeData) {
                config.customizeData(data);
            }

            var mergeCells = function (row, colspan) {
                var mergeCells = $('mergeCells', rels);

                mergeCells[0].appendChild(_createNode(rels, 'mergeCell', {
                    attr: {
                        ref: 'A' + row + ':' + createCellPos(colspan) + row
                    }
                }));
                mergeCells.attr('count', parseFloat(mergeCells.attr('count')) + 1);
                $('row:eq(' + (row - 1) + ') c', rels).attr('s', '51'); // centre
            };

            // Title and top messages
            var exportInfo = dt.buttons.exportInfo(config);
            if (exportInfo.title) {
                addRow([exportInfo.title], rowPos);
                mergeCells(rowPos, data.header.length - 1);
            }

            if (exportInfo.messageTop) {
                addRow([exportInfo.messageTop], rowPos);
                mergeCells(rowPos, data.header.length - 1);
            }


            // Table itself
            if (config.header) {
                addRow(data.header, rowPos);
                $('row:last c', rels).attr('s', '2'); // bold
            }

            dataStartRow = rowPos;

            for (var n = 0, ie = data.body.length; n < ie; n++) {
                addRow(data.body[n], rowPos);
            }

            dataEndRow = rowPos;

            if (config.footer && data.footer) {
                addRow(data.footer, rowPos);
                $('row:last c', rels).attr('s', '2'); // bold
            }

            // Below the table
            if (exportInfo.messageBottom) {
                addRow([exportInfo.messageBottom], rowPos);
                mergeCells(rowPos, data.header.length - 1);
            }

            // Set column widths
            var cols = _createNode(rels, 'cols');
            $('worksheet', rels).prepend(cols);

            for (var i = 0, ien = data.header.length; i < ien; i++) {
                cols.appendChild(_createNode(rels, 'col', {
                    attr: {
                        min: i + 1,
                        max: i + 1,
                        width: _excelColWidth(data, i),
                        customWidth: 1
                    }
                }));
            }

            // Workbook modifications
            var workbook = xlsx.xl['workbook.xml'];

            $('sheets sheet', workbook).attr('name', _sheetname(config));

            // Auto filter for columns
            if (config.autoFilter) {
                $('mergeCells', rels).before(_createNode(rels, 'autoFilter', {
                    attr: {
                        ref: 'A' + dataStartRow + ':' + createCellPos(data.header.length - 1) + dataEndRow
                    }
                }));

                $('definedNames', workbook).append(_createNode(workbook, 'definedName', {
                    attr: {
                        name: '_xlnm._FilterDatabase',
                        localSheetId: '0',
                        hidden: 1
                    },
                    text: _sheetname(config) + '!$A$' + dataStartRow + ':' + createCellPos(data.header.length - 1) + dataEndRow
                }));
            }

            // Let the developer customise the document if they want to
            if (config.customize) {
                config.customize(xlsx, config, dt);
            }

            // Excel doesn't like an empty mergeCells tag
            if ($('mergeCells', rels).children().length === 0) {
                $('mergeCells', rels).remove();
            }

            var jszip = _jsZip();
            var zip = new jszip();
            var zipConfig = {
                type: 'blob',
                mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
            };

            _addToZip(zip, xlsx);

            if (zip.generateAsync) {
                // JSZip 3+
                zip
                    .generateAsync(zipConfig)
                    .then(function (blob) {
                        _saveAs(blob, exportInfo.filename);
                        that.processing(false);
                    });
            } else {
                // JSZip 2.5
                _saveAs(
                    zip.generate(zipConfig),
                    exportInfo.filename
                );
                this.processing(false);
            }
        },

        filename: '*',

        extension: '.xlsx',

        exportOptions: {},

        header: true,

        footer: false,

        title: '*',

        messageTop: '*',

        messageBottom: '*',

        createEmptyCells: false,

        autoFilter: false,

        sheetName: ''
    };

//
// PDF export - using pdfMake - http://pdfmake.org
//
    DataTable.ext.buttons.pdfHtml5 = {
        className: 'buttons-pdf buttons-html5',

        available: function () {
            return window.FileReader !== undefined && _pdfMake();
        },

        text: function (dt) {
            return dt.i18n('buttons.pdf', 'PDF');
        },

        action: function (e, dt, button, config) {
            this.processing(true);

            var that = this;
            var data = dt.buttons.exportData(config.exportOptions);
            var info = dt.buttons.exportInfo(config);
            var rows = [];

            if (config.header) {
                rows.push($.map(data.header, function (d) {
                    return {
                        text: typeof d === 'string' ? d : d + '',
                        style: 'tableHeader'
                    };
                }));
            }

            for (var i = 0, ien = data.body.length; i < ien; i++) {
                rows.push($.map(data.body[i], function (d) {
                    if (d === null || d === undefined) {
                        d = '';
                    }
                    return {
                        text: typeof d === 'string' ? d : d + '',
                        style: i % 2 ? 'tableBodyEven' : 'tableBodyOdd'
                    };
                }));
            }

            if (config.footer && data.footer) {
                rows.push($.map(data.footer, function (d) {
                    return {
                        text: typeof d === 'string' ? d : d + '',
                        style: 'tableFooter'
                    };
                }));
            }

            var doc = {
                pageSize: config.pageSize,
                pageOrientation: config.orientation,
                content: [
                    {
                        table: {
                            headerRows: 1,
                            body: rows
                        },
                        layout: 'noBorders'
                    }
                ],
                styles: {
                    tableHeader: {
                        bold: true,
                        fontSize: 11,
                        color: 'white',
                        fillColor: '#2d4154',
                        alignment: 'center'
                    },
                    tableBodyEven: {},
                    tableBodyOdd: {
                        fillColor: '#f3f3f3'
                    },
                    tableFooter: {
                        bold: true,
                        fontSize: 11,
                        color: 'white',
                        fillColor: '#2d4154'
                    },
                    title: {
                        alignment: 'center',
                        fontSize: 15
                    },
                    message: {}
                },
                defaultStyle: {
                    fontSize: 10
                }
            };

            if (info.messageTop) {
                doc.content.unshift({
                    text: info.messageTop,
                    style: 'message',
                    margin: [0, 0, 0, 12]
                });
            }

            if (info.messageBottom) {
                doc.content.push({
                    text: info.messageBottom,
                    style: 'message',
                    margin: [0, 0, 0, 12]
                });
            }

            if (info.title) {
                doc.content.unshift({
                    text: info.title,
                    style: 'title',
                    margin: [0, 0, 0, 12]
                });
            }

            if (config.customize) {
                config.customize(doc, config, dt);
            }

            var pdf = _pdfMake().createPdf(doc);

            if (config.download === 'open' && !_isDuffSafari()) {
                pdf.open();
            } else {
                pdf.download(info.filename);
            }

            this.processing(false);
        },

        title: '*',

        filename: '*',

        extension: '.pdf',

        exportOptions: {},

        orientation: 'portrait',

        pageSize: 'A4',

        header: true,

        footer: false,

        messageTop: '*',

        messageBottom: '*',

        customize: null,

        download: 'download'
    };


    return DataTable.Buttons;
}));
